﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using System.Threading.Tasks;

namespace FrogEngine
{
	public class GamePadInputService : GameEntityServiceBase<IHandlesGamePadInput>, IGamepadInputService
	{
		public GamePadInputService(IEngine engine)
			: base(engine)
		{
			serviceOrder = 1;
			padCapabilities = new Dictionary<PlayerIndex, GamePadCapabilities>();
			currentStates = new Dictionary<PlayerIndex, GamePadState>();
			previousStates = new Dictionary<PlayerIndex, GamePadState>();

			padCapabilities.Add(PlayerIndex.One, GamePad.GetCapabilities(PlayerIndex.One.ToXNAPI()));
			padCapabilities.Add(PlayerIndex.Two, GamePad.GetCapabilities(PlayerIndex.Two.ToXNAPI()));
			padCapabilities.Add(PlayerIndex.Three, GamePad.GetCapabilities(PlayerIndex.Three.ToXNAPI()));
			padCapabilities.Add(PlayerIndex.Four, GamePad.GetCapabilities(PlayerIndex.Four.ToXNAPI()));

			previousStates.Add(PlayerIndex.One, GamePad.GetState(PlayerIndex.One.ToXNAPI()));
			previousStates.Add(PlayerIndex.Two, GamePad.GetState(PlayerIndex.Two.ToXNAPI()));
			previousStates.Add(PlayerIndex.Three, GamePad.GetState(PlayerIndex.Three.ToXNAPI()));
			previousStates.Add(PlayerIndex.Four, GamePad.GetState(PlayerIndex.Four.ToXNAPI()));

			currentStates.Add(PlayerIndex.One, GamePad.GetState(PlayerIndex.One.ToXNAPI()));
			currentStates.Add(PlayerIndex.Two, GamePad.GetState(PlayerIndex.Two.ToXNAPI()));
			currentStates.Add(PlayerIndex.Three, GamePad.GetState(PlayerIndex.Three.ToXNAPI()));
			currentStates.Add(PlayerIndex.Four, GamePad.GetState(PlayerIndex.Four.ToXNAPI()));
		}

		private Dictionary<PlayerIndex, GamePadCapabilities> padCapabilities;
		public IDictionary<PlayerIndex, GamePadCapabilities> PadCapabilities
		{
			get { return padCapabilities; }
		}

		private Dictionary<PlayerIndex, GamePadState> currentStates;
		public IDictionary<PlayerIndex, GamePadState> CurrentStates
		{
			get { return currentStates; }
		}

		private Dictionary<PlayerIndex, GamePadState> previousStates;
		public IDictionary<PlayerIndex, GamePadState> PreviousStates
		{
			get { return previousStates; }
		}

		public override bool Subscribe(IGameEntity entity)
		{
			if (base.Subscribe(entity))
			{
				return true;
			}
			else
				throw new EntityCompatibilityException(entity.GetType(), this.GetType());
		}

		public override bool Unsubscribe(IGameEntity entity)
		{
			if (base.Unsubscribe(entity))
				return true;
			else
				return false;
		}

		public void Update(GameTime gameTime)
		{
			if (isPaused)
				return;

			if (!currentStates[PlayerIndex.One].IsConnected)
				onGamePadDisconnected(PlayerIndex.One);
			if (!currentStates[PlayerIndex.Two].IsConnected)
				onGamePadDisconnected(PlayerIndex.Two);
			if (!currentStates[PlayerIndex.Three].IsConnected)
				onGamePadDisconnected(PlayerIndex.Three);
			if (!currentStates[PlayerIndex.Four].IsConnected)
				onGamePadDisconnected(PlayerIndex.Four);

			currentStates[PlayerIndex.One] = GamePad.GetState(PlayerIndex.One.ToXNAPI());
			currentStates[PlayerIndex.Two] = GamePad.GetState(PlayerIndex.Two.ToXNAPI());
			currentStates[PlayerIndex.Three] = GamePad.GetState(PlayerIndex.Three.ToXNAPI());
			currentStates[PlayerIndex.Four] = GamePad.GetState(PlayerIndex.Four.ToXNAPI());

#if WINDOWS
			Parallel.ForEach(subscribedEntities.Cast<IHandlesGamePadInput>(), new Action<IHandlesGamePadInput>(a => 
			{ 
				if (a.PlayerIndex.HasFlag(PlayerIndex.One))
					a.HandleInput(gameTime, PlayerIndex.One, CurrentStates[PlayerIndex.One], PreviousStates[PlayerIndex.One]);
				if (a.PlayerIndex.HasFlag(PlayerIndex.Two))
					a.HandleInput(gameTime, PlayerIndex.Two, CurrentStates[PlayerIndex.Two], PreviousStates[PlayerIndex.Two]);
				if (a.PlayerIndex.HasFlag(PlayerIndex.Three))
					a.HandleInput(gameTime, PlayerIndex.Three, CurrentStates[PlayerIndex.Three], PreviousStates[PlayerIndex.Three]);
				if (a.PlayerIndex.HasFlag(PlayerIndex.Four))
					a.HandleInput(gameTime, PlayerIndex.Four, CurrentStates[PlayerIndex.Four], PreviousStates[PlayerIndex.Four]);
			}));
#else
			foreach (var entity in subscribedEntities.Cast<IHandlesGamePadInput>())
				entity.HandleInput(gameTime, CurrentStates[entity.PlayerIndex], PreviousStates[entity.PlayerIndex]);
#endif

			previousStates[PlayerIndex.One] = currentStates[PlayerIndex.One];
			previousStates[PlayerIndex.Two] = currentStates[PlayerIndex.Two];
			previousStates[PlayerIndex.Three] = currentStates[PlayerIndex.Three];
			previousStates[PlayerIndex.Four] = currentStates[PlayerIndex.Four];
		}

		private void onGamePadDisconnected(PlayerIndex index)
		{
			if (GamePadDisconnected != null)
				GamePadDisconnected(this, new GamePadDisconnectedEventArgs(index));
		}

		public event EventHandler<GamePadDisconnectedEventArgs> GamePadDisconnected;

		#region [ IPausable ]
		private bool isPaused = false;
		public void Pause()
		{
			isPaused = true;
		}

		public void Resume()
		{
			isPaused = false;
		}
		#endregion
	}
}
